package wxpay

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"

	uuid "github.com/satori/go.uuid"
)

const NotifyUrl = "https://api.pubcloud.vip/mall/pay/v1/wxPayCallback"
const MchId = "1606577609"
const MchSecret = "G2ADlVDdTYeGs9MqLQrgKtfFwL5WhF8g"

type UnifyOrderReq struct {
	AppId          string `xml:"appid"`
	Body           string `xml:"body"`
	MchId          string `xml:"mch_id"`
	NonceStr       string `xml:"nonce_str"`
	NotifyUrl      string `xml:"notify_url"`
	TradeType      string `xml:"trade_type"`
	SpBillCreateIp string `xml:"spbill_create_ip"`
	TotalFee       int    `xml:"total_fee"`
	OutTradeNo     string `xml:"out_trade_no"`
	Sign           string `xml:"sign"`
	OpenId         string `xml:"openid"`
}

type WxPayResponse struct {
	ReturnCode string `xml:"return_code"`
	ReturnMsg  string `xml:"return_msg"`
	NonceStr   string `xml:"nonce_str"`
	PrepayId   string `xml:"prepay_id"`
	ResultCode string `xml:"result_code"`
	ErrCodeDes string `xml:"err_code_des"`

	//SUCCESS--支付成功
	//REFUND--转入退款
	//NOTPAY--未支付
	//CLOSED--已关闭
	//REVOKED--已撤销(刷卡支付)
	//USERPAYING--用户支付中
	//PAYERROR--支付失败(其他原因，如银行返回失败)
	//ACCEPT--已接收，等待扣款
	TradeState         string `xml:"trade_state"`
	OpenId             string `xml:"openid"`
	BankType           string `xml:"bank_type"`
	TotalFee           int    `xml:"total_fee"`
	TransactionId      string `xml:"transaction_id"`
	TimeEnd            string `xml:"time_end"`
	TradeStateDesc     string `xml:"trade_state_desc"`
	OutTradeNo         string `xml:"out_trade_no"`
	RefundCount        int    `xml:"refund_count"`
	OutRefundNo0       string `xml:"out_refund_no_0"`
	RefundFee0         int    `xml:"refund_fee_0"`
	RefundStatus0      string `xml:"refund_status_0"`
	RefundSuccessTime0 string `xml:"refund_success_time_0"`
}

func WxPayCalcSign(mReq map[string]interface{}, key string) (sign string) {
	sortedKeys := make([]string, 0)
	for k, _ := range mReq {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)
	var signString string
	for _, k := range sortedKeys {
		v := fmt.Sprintf("%v", mReq[k])
		if v != "" {
			signString = signString + k + "=" + v + "&"
		}
	}
	if key != "" {
		signString = signString + "key=" + key
	}
	md5String := md5.New()
	md5String.Write([]byte(signString))
	md5StringSum := md5String.Sum(nil)
	upperSign := strings.ToUpper(hex.EncodeToString(md5StringSum))
	return upperSign
}

func WxSecondSign(appId string, timeStamp string, nonceStr string, prepayId string) (string, error) {
	var mReqMap map[string]interface{}
	mReqMap = make(map[string]interface{}, 0)
	mReqMap["appId"] = appId
	mReqMap["timeStamp"] = timeStamp
	mReqMap["nonceStr"] = nonceStr
	mReqMap["package"] = "prepay_id=" + prepayId
	mReqMap["signType"] = "MD5"
	paySign := WxPayCalcSign(mReqMap, MchSecret)
	return paySign, nil
}

func WxUnifiedOrder(appId string, payTitle string, totalFee int, orderCode string, openId string) (WxPayResponse, error) {
	var wxPayReq UnifyOrderReq
	sourceUuid := uuid.NewV4()
	formatUuid := strings.Replace(sourceUuid.String(), "-", "", -1)
	wxPayReq.AppId = appId
	wxPayReq.Body = payTitle
	wxPayReq.MchId = MchId
	wxPayReq.NonceStr = formatUuid
	wxPayReq.NotifyUrl = NotifyUrl
	wxPayReq.TradeType = "JSAPI"
	wxPayReq.SpBillCreateIp = "127.0.0.1"
	wxPayReq.TotalFee = totalFee //单位是分，10是1毛钱
	wxPayReq.OutTradeNo = orderCode
	wxPayReq.OpenId = openId

	var mReqMap map[string]interface{}
	mReqMap = make(map[string]interface{}, 0)
	mReqMap["openid"] = wxPayReq.OpenId
	mReqMap["appid"] = wxPayReq.AppId
	mReqMap["body"] = wxPayReq.Body
	mReqMap["mch_id"] = wxPayReq.MchId
	mReqMap["notify_url"] = wxPayReq.NotifyUrl
	mReqMap["trade_type"] = wxPayReq.TradeType
	mReqMap["spbill_create_ip"] = wxPayReq.SpBillCreateIp
	mReqMap["total_fee"] = wxPayReq.TotalFee
	mReqMap["out_trade_no"] = wxPayReq.OutTradeNo
	mReqMap["nonce_str"] = wxPayReq.NonceStr
	wxPayReq.Sign = WxPayCalcSign(mReqMap, MchSecret)

	xmlReq, err := xml.Marshal(wxPayReq)
	if err != nil {
		return WxPayResponse{}, err
	}
	strReq := string(xmlReq)
	strReq = strings.Replace(strReq, "UnifyOrderReq", "xml", -1)
	xmlReq = []byte(strReq)

	request, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/pay/unifiedorder", bytes.NewReader(xmlReq))
	if err != nil {
		return WxPayResponse{}, err
	}
	request.Header.Set("Accept", "application/xml")
	request.Header.Set("Content-Type", "application/xml;charset=utf-8")

	httpClient := http.Client{}
	response, err := httpClient.Do(request)
	if err != nil {
		return WxPayResponse{}, err
	}

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return WxPayResponse{}, err
	}

	var payResponse WxPayResponse
	err = xml.Unmarshal(responseBody, &payResponse)
	if err != nil {
		return WxPayResponse{}, err
	}

	return payResponse, nil
}
